#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <signal.h>
#include <pthread.h>

#define N 128

typedef struct
{
    struct sockaddr_in cli_addr;
    int cli_accept_fd;
}MSG;

void handler(int sig)    //信号处理函数
{
    wait(NULL);
}

void* pthread_handler(void *arg)  //线程函数
{
    MSG tem_msg = *(MSG*)arg;
    ssize_t bytes = 0;
    char buf_recv[N] = "";
    //接收请求并处理
    while(1)
    {
        //接收客户端请求
        memset(buf_recv,0,N);
        if((bytes = recv(tem_msg.cli_accept_fd,buf_recv,N,0)) == -1)   
        {
            perror("fail to recv");
            exit(-1);
        }

        //退出线程处理
        if(bytes == 0 || strcmp(buf_recv,"exit") == 0)
        {
            printf("client[%d] exits!\n",ntohs(tem_msg.cli_addr.sin_port));
            break;
        }
        printf("from client[%d]:%s\n",ntohs(tem_msg.cli_addr.sin_port),buf_recv);

        //服务端处理请求
        char *buf_send = strcat(buf_recv,"[yes]");
        if(send(tem_msg.cli_accept_fd,buf_send,N,0) == -1)
        {
            perror("fail to send");
            exit(-1);
        }
    }

    //关闭副套接字，退出线程
    close(tem_msg.cli_accept_fd);
    pthread_exit(NULL);
}

int main(int argc, char const *argv[])
{
    //创建监听套接字
    int sockfd;
    if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1)
    {
        perror("fail to socket");
        exit(-1);
    }

    //绑定服务端地址和端口
    struct sockaddr_in serveraddr;
    socklen_t seraddr_len  = sizeof(serveraddr);
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    serveraddr.sin_port = htons(atoi(argv[2]));
    if(bind(sockfd,(struct sockaddr*)&serveraddr,seraddr_len) == -1)
    {
        perror("fail to bind");
        exit(-1);
    }

    //修改套接字属性，设置连接队列
    if(listen(sockfd,10) == -1)
    {
        perror("fail to listen");
        exit(-1);
    }

    int accept_fd;
    struct sockaddr_in clientaddr;
    socklen_t cliaddr_len = sizeof(clientaddr);

    //多进程实现并发
    #if 0
    //使用信号，处理僵尸进程
    signal(SIGCHLD,handler);
    while(1)
    {
        //阻塞等待客户端连接
        if((accept_fd = accept(sockfd,(struct sockaddr*)&clientaddr,&cliaddr_len)) == -1)
        {
            perror("fail to accept");
            exit(-1);
        }
        printf("client[ip port]:%s %d\n",inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));
        
        //创建子进程，实现并发服务器
        pid_t my_pid;
        if((my_pid = fork()) == -1)
        {
            perror("fail to fork");
            exit(-1);
        }
        if(my_pid == 0)
        {
            int bytes = 0;
            char buf_recv[N] = "";
            //接收请求并处理
            while(1)
            {
                //接收客户端请求
                memset(buf_recv,0,N);
                //成功接收数据时，bytes == N(128)???why
                if((bytes = recv(accept_fd,buf_recv,N,0)) == -1)   
                {
                    perror("fail to recv");
                    exit(-1);
                }

                //退出子进程处理
                if(bytes == 0 || strcmp(buf_recv,"exit") == 0)
                {
                    printf("client[%d] exits!\n",ntohs(clientaddr.sin_port));
                    break;
                }
                printf("from client[%d]:%s\n",ntohs(clientaddr.sin_port),buf_recv);

                //服务端处理请求
                char *buf_send = strcat(buf_recv,"[yes]");
                if(send(accept_fd,buf_send,N,0) == -1)
                {
                    perror("fail to send");
                    exit(-1);
                }
            }

            //关闭副套接字
            close(accept_fd);
            exit(0);
        }
    }
    #endif

    //多线程实现并发
    #if 1
    while(1)
    {
        //阻塞等待客户端连接
        if((accept_fd = accept(sockfd,(struct sockaddr*)&clientaddr,&cliaddr_len)) == -1)
        {
            perror("fail to accept");
            exit(-1);
        }
        printf("client[ip port]:%s %d\n",inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));
        
        //创建子线程，实现并发服务器
        pthread_t pthread_id;
        MSG msg = {clientaddr,accept_fd};
        if(pthread_create(&pthread_id,NULL,pthread_handler,&msg) != 0)
        {
            perror("fail to pthread_create");
            exit(-1);
        }

        //设置线程分离态，便于资源回收
        pthread_detach(pthread_id);
    }
    #endif

    //关闭监听套接字
    close(sockfd);
    return 0;
}
